/*------------------------------------------------------------------------------*\
**   Copyright 2008 Jonhnny Weslley                                             **
**                                                                              **
**   Licensed under the Apache License, Version 2.0 (the "License");            **
**   you may not use this file except in compliance with the License.           **
**   You may obtain a copy of the License at                                    **
**                                                                              **
**       http://www.apache.org/licenses/LICENSE-2.0                             **
**                                                                              **
**   Unless required by applicable law or agreed to in writing, software        **
**   distributed under the License is distributed on an "AS IS" BASIS,          **
**   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   **
**   See the License for the specific language governing permissions and        **
**   limitations under the License.                                             **
\*------------------------------------------------------------------------------*/

package shibernate

import scala.collection.mutable.{ListBuffer, Map}
import org.hibernate.{Criteria, Session}
import org.hibernate.criterion.Projections
import org.hibernate.metadata.ClassMetadata

object Search {

  def resultCount[A](session: Session, entityClass: Class[A], param: Parameter) = {
    val criteria = session createCriteria entityClass
    criteria setProjection Projections.rowCount
    val results = doSearch(criteria, session, entityClass, param).iterator
    results.ensuring(results.hasNext, "No results").next.asInstanceOf[Int]
  }

  def apply[A](session: Session, entityClass: Class[A],
            param: Parameter, firstResult: Int, maxResults: Int) = {
    val criteria = session createCriteria entityClass
    criteria setFirstResult firstResult
    criteria setMaxResults maxResults
    doSearch(criteria, session, entityClass, param)
  }

  def apply[A](session: Session, entityClass: Class[A], param: Parameter) = {
    val criteria = session createCriteria entityClass
    doSearch(criteria, session, entityClass, param)
  }

  def apply[A](criteriaFactory: (Session, Class[A]) => Criteria)
           (session: Session, entityClass: Class[A], param: Parameter) = {
    val criteria = criteriaFactory(session, entityClass)
    doSearch(criteria, session, entityClass, param)
  }

  private def toList(param: Parameter) = {
    val list = new ListBuffer[Parameter]
    param match {
      case null => list
      case And(p1,p2) => list + p1 + p2
      case p @ _ => list + p
    }
    list
  }

  private def doSearch[A](criteria: Criteria, session: Session, 
                       entityClass: Class[A], param: Parameter) = {
    val params = toList(param)
    configureCriteria(criteria, session, entityClass, params)
    criteria.list.asInstanceOf[java.util.List[A]]
  }

  private def configureCriteria(criteria: Criteria, session: Session, entityClass: Class[_], 
                             params: ListBuffer[Parameter]) = {
    if (params != null) {
      val table: Map[String, Criteria] = Map.empty
      for (param <- params) {
        if (!param.isLogic) {
          val meta = session.getSessionFactory.getClassMetadata(entityClass)
          if (meta.getIdentifierPropertyName ne param.prefix) {
            val tpe = meta.getPropertyType(param.prefix)
            param.isComponent = tpe.isComponentType
          }
        }
        param.process(criteria, table)
      }
    }
  }

}
